package drone.basis.util ;

import java.io.File ;
import java.math.BigDecimal ;
import java.math.BigInteger ;
import java.net.InetAddress ;
import java.nio.ByteBuffer ;
import java.util.ArrayList ;
import java.util.List ;
import java.util.regex.Pattern ;

import javax.servlet.http.HttpServletRequest ;

import com.maxmind.geoip2.DatabaseReader ;
import com.maxmind.geoip2.model.CityResponse ;
import com.maxmind.geoip2.model.CountryResponse ;
import com.maxmind.geoip2.record.City ;
import com.maxmind.geoip2.record.Country ;
import com.maxmind.geoip2.record.Subdivision ;

import drone.basis.GlobalConfig ;

public class IpUtil
{

	public final static String NEWYORK = "New York" ;

	public final static String PENNSYLVANIA = "Pennsylvania" ;

	public final static String CALIFORNIA = "California" ;

	public final static String OREGON = "Oregon" ;

	public final static String VIRGINIA = "Virginia" ;

	public final static String WASHINGTON = "Washington" ;

	private static String cityPath = "/usr/local/tomcat7/GeoIP2-City.mmdb" ;

	private static String countryPath = "/usr/local/tomcat7/GeoIP2-Country.mmdb" ;
	static
	{
		if ( ! "linux".equalsIgnoreCase ( System.getProperty ( "os.name" ) ) )
		{
			cityPath = "D:\\I-P\\GeoIP2-City.mmdb" ;
			countryPath = "D:\\I-P\\GeoIP2-Country.mmdb" ;
		} else
		{
			cityPath = "/usr/local/tomcat9/GeoIP2-City.mmdb" ;
			countryPath = "/usr/local/tomcat9/GeoIP2-Country.mmdb" ;
		}
	}

	public static synchronized String getIp (
			HttpServletRequest request
	)
	{
		String ip = request.getHeader ( "X-Forwarded-For" ) ;
		if ( StringUtil.isEmpty ( ip ) || "unknown".equalsIgnoreCase ( ip ) )
		{
			ip = request.getHeader ( "Proxy-Client-IP" ) ;
		}
		if ( StringUtil.isEmpty ( ip ) || "unknown".equalsIgnoreCase ( ip ) )
		{
			ip = request.getHeader ( "WL-Proxy-Client-IP" ) ;
		}
		if ( StringUtil.isEmpty ( ip ) || "unknown".equalsIgnoreCase ( ip ) )
		{
			ip = request.getHeader ( "HTTP_CLIENT_IP" ) ;
		}
		if ( StringUtil.isEmpty ( ip ) || "unknown".equalsIgnoreCase ( ip ) )
		{
			ip = request.getHeader ( "HTTP_X_FORWARDED_FOR" ) ;
		}
		if ( StringUtil.isEmpty ( ip ) || "unknown".equalsIgnoreCase ( ip ) )
		{
			ip = request.getRemoteAddr ( ) ;
		}
		return ip ;
	}

	public static synchronized String getCommonIp (
			String ip
	)
	{
		if ( StringUtil.isEmpty ( ip ) )
		{
			return GlobalConfig.IP ;
		}
		if ( ip.indexOf ( "," ) > 0 )
		{
			return ip.substring ( 0 , ip.indexOf ( "," ) ) ;
		}
		return ip ;
	}

	public static Boolean isIp (
			String ip
	)
	{
		if ( isIpv4 ( ip ) || isIpv6 ( ip ) )
		{
			return true ;
		}
		return false ;
	}

	public static Boolean isIpv4 (
			String ip
	)
	{
		if ( StringUtil.isEmpty ( ip ) )
		{
			return false ;
		}
		if ( Pattern.compile ( "^(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])(\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])){3}$" ).matcher ( ip ).matches ( ) )
		{
			return true ;
		}
		return false ;
	}

	public static Boolean isIpv6 (
			String ip
	)
	{
		if ( StringUtil.isEmpty ( ip ) )
		{
			return false ;
		}
		if ( Pattern
				.compile (
						"^((([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){1,7}:)|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}(:[0-9A-Fa-f]{1,4}){1,2})|(([0-9A-Fa-f]{1,4}:){4}(:[0-9A-Fa-f]{1,4}){1,3})|(([0-9A-Fa-f]{1,4}:){3}(:[0-9A-Fa-f]{1,4}){1,4})|(([0-9A-Fa-f]{1,4}:){2}(:[0-9A-Fa-f]{1,4}){1,5})|([0-9A-Fa-f]{1,4}:(:[0-9A-Fa-f]{1,4}){1,6})|(:(:[0-9A-Fa-f]{1,4}){1,7})|(([0-9A-Fa-f]{1,4}:){6}(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])(\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])){3})|(([0-9A-Fa-f]{1,4}:){5}:(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])(\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])){3})|(([0-9A-Fa-f]{1,4}:){4}(:[0-9A-Fa-f]{1,4}){0,1}:(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])(\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])){3})|(([0-9A-Fa-f]{1,4}:){3}(:[0-9A-Fa-f]{1,4}){0,2}:(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])(\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])){3})|(([0-9A-Fa-f]{1,4}:){2}(:[0-9A-Fa-f]{1,4}){0,3}:(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])(\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])){3})|([0-9A-Fa-f]{1,4}:(:[0-9A-Fa-f]{1,4}){0,4}:(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])(\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])){3})|(:(:[0-9A-Fa-f]{1,4}){0,5}:(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])(\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])){3}))$" )
				.matcher ( ip ).matches ( ) )
		{
			return true ;
		}
		return false ;
	}

	public static BigDecimal ip2number (
			String ip
	)
	{
		if ( isIpv4 ( ip ) )
		{
			return ipv42number ( ip ) ;
		}
		if ( isIpv6 ( ip ) )
		{
			return ipv62number ( ip ) ;
		}
		return new BigDecimal ( 1 ) ;
	}

	/** 将IPv4字符串转化为对应的长整型整数 */
	public static BigDecimal ipv42number (
			String ipv4
	)
	{
		try
		{
			String [ ] p4 = ipv4.split ( "\\." ) ;
			long ipInt = 0 ;
			long part = Long.valueOf ( p4 [ 0 ] ) ;
			ipInt = ipInt | ( part << 24 ) ;
			part = Long.valueOf ( p4 [ 1 ] ) ;
			ipInt = ipInt | ( part << 16 ) ;
			part = Long.valueOf ( p4 [ 2 ] ) ;
			ipInt = ipInt | ( part << 8 ) ;
			part = Long.valueOf ( p4 [ 3 ] ) ;
			ipInt = ipInt | ( part ) ;
			return new BigDecimal ( ipInt ) ;
		} catch ( Exception e )
		{
			return new BigDecimal ( 1 ) ;
		}
	}

	/** 将IP地址长整型数值转化为IPv4字符串 */
	public static String number2ipv4 (
			long ipNumber
	)
	{
		String ipStr = String.valueOf ( ( ipNumber & 0xff000000 ) >> 24 ) ;
		ipStr += "." + ( ( ipNumber & 0xff0000 ) >> 16 ) ;
		ipStr += "." + ( ( ipNumber & 0xff00 ) >> 8 ) ;
		ipStr += "." + ( ipNumber & 0xff ) ;
		return ipStr ;
	}

	/** 将IP地址长整型数值转化为IPv6字符串 */
	public static BigDecimal ipv62number (
			String ipv6
	)
	{
		byte [ ] bytes = null ;
		try
		{
			bytes = InetAddress.getByName ( ipv6 ).getAddress ( ) ;
			return new BigDecimal ( new BigInteger ( 1 , bytes ) ) ;
		} catch ( Exception e )
		{
			return new BigDecimal ( new BigInteger ( new Integer ( 1 ).toString ( ) ) ) ;
		}
	}

	/** 将IPv6字符串转化为对应的长整型整数 */
	public static String number2ipv6 (
			BigInteger ipNumber
	)
	{
		String ipString = "" ;
		BigInteger a = new BigInteger ( "FFFF" , 16 ) ;
		for ( int i = 0 ; i < 8 ; i ++ )
		{
			ipString = ipNumber.and ( a ).toString ( 16 ) + ":" + ipString ;
			ipNumber = ipNumber.shiftRight ( 16 ) ;
		}
		return ipString.substring ( 0 , ipString.length ( ) - 1 ) ;
	}

	/** 根据掩码计算ip起始点,兼容ipv4和ipv6 */
	public static String calcStart (
			String ipAddress ,
			int maskLength
	)
	{
		InetAddress inetAddress = null ;
		try
		{
			inetAddress = InetAddress.getByName ( ipAddress ) ;
			ByteBuffer maskBuffer ;
			int targetSize ;
			if ( inetAddress.getAddress ( ).length == 4 )
			{
				maskBuffer = ByteBuffer.allocate ( 4 ).putInt ( - 1 ) ;
				targetSize = 4 ;
			} else
			{
				maskBuffer = ByteBuffer.allocate ( 16 ).putLong ( - 1L ).putLong ( - 1L ) ;
				targetSize = 16 ;
			}
			BigInteger mask = ( new BigInteger ( 1 , maskBuffer.array ( ) ) ).not ( ).shiftRight ( maskLength ) ;
			ByteBuffer buffer = ByteBuffer.wrap ( inetAddress.getAddress ( ) ) ;
			BigInteger ipVal = new BigInteger ( 1 , buffer.array ( ) ) ;
			BigInteger startIp = ipVal.and ( mask ) ;

			int counter = 0 ;
			List < Byte > newArr = new ArrayList < Byte > ( ) ;
			while ( counter < targetSize && ( startIp.toByteArray ( ).length - 1 - counter >= 0 ) )
			{
				newArr.add ( 0 , startIp.toByteArray ( ) [ startIp.toByteArray ( ).length - 1 - counter ] ) ;
				counter ++ ;
			}
			int size = newArr.size ( ) ;
			for ( int i = 0 ; i < ( targetSize - size ) ; i ++ )
			{
				newArr.add ( 0 , ( byte ) 0 ) ;
			}
			byte [ ] ret = new byte [ newArr.size ( ) ] ;
			for ( int i = 0 ; i < newArr.size ( ) ; i ++ )
			{
				ret [ i ] = newArr.get ( i ) ;
			}
			return InetAddress.getByAddress ( ret ).getHostAddress ( ) ;
		} catch ( Exception e )
		{
			return ipAddress ;
		}
	}

	/** 根据掩码计算ip结束点,兼容ipv4和ipv6 */
	public static String calcEnd (
			String ipAddress ,
			int maskLength
	)
	{
		InetAddress inetAddress = null ;
		try
		{
			inetAddress = InetAddress.getByName ( ipAddress ) ;
			ByteBuffer maskBuffer ;
			int targetSize ;
			if ( inetAddress.getAddress ( ).length == 4 )
			{
				maskBuffer = ByteBuffer.allocate ( 4 ).putInt ( - 1 ) ;
				targetSize = 4 ;
			} else
			{
				maskBuffer = ByteBuffer.allocate ( 16 ).putLong ( - 1L ).putLong ( - 1L ) ;
				targetSize = 16 ;
			}
			BigInteger mask = ( new BigInteger ( 1 , maskBuffer.array ( ) ) ).not ( ).shiftRight ( maskLength ) ;
			ByteBuffer buffer = ByteBuffer.wrap ( inetAddress.getAddress ( ) ) ;
			BigInteger ipVal = new BigInteger ( 1 , buffer.array ( ) ) ;
			BigInteger endIp = ipVal.and ( mask ).add ( mask.not ( ) ) ;

			int counter = 0 ;
			List < Byte > newArr = new ArrayList < Byte > ( ) ;
			while ( counter < targetSize && ( endIp.toByteArray ( ).length - 1 - counter >= 0 ) )
			{
				newArr.add ( 0 , endIp.toByteArray ( ) [ endIp.toByteArray ( ).length - 1 - counter ] ) ;
				counter ++ ;
			}
			int size = newArr.size ( ) ;
			for ( int i = 0 ; i < ( targetSize - size ) ; i ++ )
			{
				newArr.add ( 0 , ( byte ) 0 ) ;
			}
			byte [ ] ret = new byte [ newArr.size ( ) ] ;
			for ( int i = 0 ; i < newArr.size ( ) ; i ++ )
			{
				ret [ i ] = newArr.get ( i ) ;
			}
			return InetAddress.getByAddress ( ret ).getHostAddress ( ) ;
		} catch ( Exception e )
		{
			return ipAddress ;
		}
	}

	public static synchronized String getCountryName (
			String ip
	)
	{
		try
		{
			DatabaseReader reader = new DatabaseReader.Builder ( new File ( countryPath ) ).build ( ) ;
			CountryResponse response = reader.country ( InetAddress.getByName ( ip ) ) ;
			Country country = response.getCountry ( ) ;
			if ( null == country.getName ( ) )
			{
				return GlobalConfig.UNKNOWN ;
			}
			return country.getName ( ) ;
		} catch ( Exception e )
		{
			return GlobalConfig.UNKNOWN ;
		}
	}

	public static synchronized String getCountryCode (
			String ip
	)
	{
		try
		{
			DatabaseReader reader = new DatabaseReader.Builder ( new File ( countryPath ) ).build ( ) ;
			CountryResponse response = reader.country ( InetAddress.getByName ( ip ) ) ;
			Country country = response.getCountry ( ) ;
			if ( null == country.getIsoCode ( ) )
			{
				return GlobalConfig.UNCODE ;
			}
			return country.getIsoCode ( ) ;
		} catch ( Exception e )
		{
			return GlobalConfig.UNCODE ;
		}
	}

	public static synchronized String getStateName (
			String ip
	)
	{
		try
		{
			DatabaseReader reader = new DatabaseReader.Builder ( new File ( cityPath ) ).build ( ) ;
			CityResponse response = reader.city ( InetAddress.getByName ( ip ) ) ;
			List < Subdivision > list = response.getSubdivisions ( ) ;
			if ( null == list.get ( 0 ) )
			{
				return "unknown" ;
			}
			return list.get ( 0 ).toString ( ) ;
		} catch ( Exception e )
		{
			return "unknown" ;
		}
	}

	public static synchronized String getCityName (
			String ip
	)
	{
		try
		{
			DatabaseReader reader = new DatabaseReader.Builder ( new File ( cityPath ) ).build ( ) ;
			CityResponse response = reader.city ( InetAddress.getByName ( ip ) ) ;
			City city = response.getCity ( ) ;
			if ( null == city.getName ( ) )
			{
				return "unknown" ;
			}
			return city.getName ( ) ;
		} catch ( Exception e )
		{
			return "unknown" ;
		}
	}

	public static synchronized boolean isCountryName (
			String ip ,
			String name
	)
	{
		try
		{
			DatabaseReader reader = new DatabaseReader.Builder ( new File ( countryPath ) ).build ( ) ;
			CountryResponse response = reader.country ( InetAddress.getByName ( ip ) ) ;
			Country country = response.getCountry ( ) ;
			if ( null == country.getName ( ) )
			{
				return false ;
			}
			return name.equalsIgnoreCase ( country.getName ( ) ) ;
		} catch ( Exception e )
		{
			return false ;
		}
	}

	public static synchronized boolean isCountryCode (
			String ip ,
			String code
	)
	{
		try
		{
			DatabaseReader reader = new DatabaseReader.Builder ( new File ( countryPath ) ).build ( ) ;
			CountryResponse response = reader.country ( InetAddress.getByName ( ip ) ) ;
			Country country = response.getCountry ( ) ;
			if ( null == country.getIsoCode ( ) )
			{
				return false ;
			}
			return code.equalsIgnoreCase ( country.getIsoCode ( ) ) ;
		} catch ( Exception e )
		{
			return false ;
		}
	}

	public static synchronized boolean isStateName (
			String ip ,
			String name
	)
	{
		try
		{
			DatabaseReader reader = new DatabaseReader.Builder ( new File ( cityPath ) ).build ( ) ;
			CityResponse response = reader.city ( InetAddress.getByName ( ip ) ) ;
			List < Subdivision > list = response.getSubdivisions ( ) ;
			if ( null == list.get ( 0 ) )
			{
				return false ;
			}
			return name.equalsIgnoreCase ( list.get ( 0 ).toString ( ) ) ;
		} catch ( Exception e )
		{
			return false ;
		}
	}

	public static synchronized boolean isCityName (
			String ip ,
			String name
	)
	{
		try
		{
			DatabaseReader reader = new DatabaseReader.Builder ( new File ( cityPath ) ).build ( ) ;
			CityResponse response = reader.city ( InetAddress.getByName ( ip ) ) ;
			City city = response.getCity ( ) ;
			if ( null == city.getName ( ) )
			{
				return false ;
			}
			return name.equalsIgnoreCase ( city.getName ( ) ) ;
		} catch ( Exception e )
		{
			return false ;
		}
	}

	

	public static void main (
			String [ ] args
	)
	{
		System.out.println ( IpUtil.isIp ( "0:0:0:0:0:0:0:1" ) ) ;

		// System.out.println(isIpv6("2001:0DB8:0000:0023:0008:0800:200C:417A"));
		// System.out.println(isIpv6("2001:DB8:0:23:8:800:200C:417A"));
		// System.out.println(isIpv6("2001:DB8:0:23:8:800:192.1.0.0"));
		// System.out.println(isIpv6("2001:DB8:0::800:192.1.0.0"));
		// System.out.println(isIpv6("2001:DB8:0:23::192.1.0.0"));
		// System.out.println(isIpv6("::192.1.0.0"));
		// System.out.println(isIpv6("1:af::3"));
		// System.out.println(isIpv6("1:af::"));
		// System.out.println(isIpv6("::1:af:0"));
		// System.out.println(isIpv6("::0"));
		// System.out.println("---------------------");
		// System.out.println(isIpv6("+2001:0DB8:0000:0023:0008:0800:200C:417A"));
		// System.out.println(isIpv6("2001:0DB8:0z00:0023:0008:0800:200C:417A"));
		// System.out.println(isIpv6("2001:DB8:0:23:800::192.1.0.0.1"));
		// System.out.println(isIpv6("2001:DB8::23::800:192.1.0.0"));
		// System.out.println(isIpv6(":::"));
		// System.out.println(isIpv6("1:::2"));
		//
		// System.out.println("---------------------");
		// System.out.println(isIpv4("1.1.0.1"));
		// System.out.println(isIpv4("123.1.0.19"));
		// System.out.println(isIpv4("255.255.255.255"));
		// System.out.println(isIpv4("0.0.0.0"));
		// System.out.println("---------------------");
		// System.out.println(isIpv4("-1.1.0.1"));
		// System.out.println(isIpv4("1.1b.0.1"));
		// System.out.println(isIpv4("1.01.0.1"));
		// System.out.println(isIpv4("1.1.300.1"));
		// System.out.println(isIpv4("1.1..1"));
		//
		// System.out.println(IpUtil.ipv62number("FFFF::E3F1:2345:1111"));
		// System.out.println(IpUtil.number2ipv6(new
		// BigInteger("340277174624079928635746327560257343761")));
		// System.out.println("===========================");
		// for (int i = 0; i < 10; i++) {
		//
		// BigInteger b = new
		// BigInteger(NumberUtil.random(IpUtil.ipv62number("FFFF::E3F1:2345:1111").toBigInteger(),
		// IpUtil.ipv62number("FFFF::E3F1:2345:68e9").toBigInteger()) + "");
		// System.out.println(b);
		// System.out.println(IpUtil.number2ipv6(b));
		// }
		// System.out.println("===========================");
		// System.out.println(IpUtil.ipv62number("FFFF::E3F1:2345:68e9"));
		// System.out.println(IpUtil.number2ipv6(new
		// BigInteger("340277174624079928635746327560257366249")));
		//
		// System.out.println("===========================");
		// System.out.println("===========================");
		// System.out.println("===========================");
		// System.out.println("===========================");
		// System.out.println("===========================");
		//
		// System.out.println(IpUtil.ipv42number("127.0.0.1"));
		// System.out.println(IpUtil.number2ipv4(new Long("2130706433")));
		// System.out.println("===========================");
		// for (int i = 0; i < 10; i++) {
		//
		// long b =
		// NumberUtil.random(IpUtil.ipv42number("127.0.0.1").longValue(),
		// IpUtil.ipv42number("127.0.2.1").longValue());
		// System.out.println(b);
		// System.out.println(IpUtil.number2ipv4(b));
		// }
		// System.out.println("===========================");
		// System.out.println(IpUtil.ipv42number("127.0.2.1"));
		// System.out.println(IpUtil.number2ipv4(new Long("2130706945")));
		//
		// long s = ipv42number("127.0.0.1").longValue();
		// System.out.println(s);
		// String b = number2ipv4(s);
		// System.out.println(b);
		//
		// BigInteger a = ipv62number("FFFF::E3F1:2345:68e9").toBigInteger();
		// System.out.println(a);
		// String c = number2ipv6(a);
		// System.out.println(c);
		//
		// System.out.println("===================");
		// String start = calcStart("::E3F1:2345:68e9", 101);
		// String end = calcEnd("::E3F1:2345:68e9", 101);
		// // String start=calcStart("10.22.1.0", 24);
		// // String end=calcEnd("10.22.1.0", 24);
		// System.out.println(start);
		// System.out.println(end);
		//
		// System.out.println("===================");
		//
		// BigInteger ss = ipv62number(start).toBigInteger();
		// BigInteger ee = ipv62number(end).toBigInteger();
		//
		// for (int i = 0; i < 10; i++) {
		// BigInteger rand = NumberUtil.random(ss, ee);
		// String bb = number2ipv6(rand);
		// System.out.println(bb);
		// }

	}
}
